/*
 * Copyright 2023-2025 Dozingfiretruck
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "nes.h"
#include "osal_addr.h"
#include "ili9341_lcd.h"
#include "soc_osal.h"
#include "gpio.h"
#include "pinctrl.h"

/* memory */
void *nes_malloc(int num){
    unsigned int osal_gfp_flag = 0;
    return osal_kmalloc(num, osal_gfp_flag);
}

void nes_free(void *address){
    osal_kfree(address);
}

void *nes_memcpy(void *str1, const void *str2, size_t n){
    char *dest = (char *)str1;
    const char *src = (const char *)str2;
    
    if (dest == NULL || src == NULL || n == 0) {
        return str1;
    }
    
    // 优化：检查内存重叠
    if (dest < src || dest >= src + n) {
        // 正向拷贝
        for (size_t i = 0; i < n; i++) {
            dest[i] = src[i];
        }
    } else {
        // 反向拷贝（处理重叠情况）
        for (size_t i = n; i > 0; i--) {
            dest[i-1] = src[i-1];
        }
    }
    
    return str1;
}

void *nes_memset(void *str, int c, size_t n){
    unsigned char *ptr = (unsigned char *)str;
    unsigned char value = (unsigned char)c;
    
    if (ptr == NULL || n == 0) {
        return str;
    }
    
    // 优化：按字节设置
    for (size_t i = 0; i < n; i++) {
        ptr[i] = value;
    }
    
    return str;
}

int nes_memcmp(const void *str1, const void *str2, size_t n){
    const unsigned char *p1 = (const unsigned char *)str1;
    const unsigned char *p2 = (const unsigned char *)str2;
    
    if (str1 == NULL || str2 == NULL) {
        return 0;
    }
    
    for (size_t i = 0; i < n; i++) {
        if (p1[i] != p2[i]) {
            return (p1[i] < p2[i]) ? -1 : 1;
        }
    }
    
    return 0;
}

#if (NES_USE_FS == 1)
/* io */
FILE *nes_fopen(const char * filename, const char * mode ){
    // 在嵌入式环境中，文件系统操作通常不可用
    // 因为NES ROM数据已经通过二进制数组加载，所以这些函数可以返回空实现
    (void)filename;
    (void)mode;
    return NULL;  // 表示文件打开失败
}

size_t nes_fread(void *ptr, size_t size, size_t nmemb, FILE *stream){
    // 在嵌入式环境中，ROM数据通过内存数组访问，不需要文件读取
    (void)ptr;
    (void)size;
    (void)nmemb;
    (void)stream;
    return 0;  // 返回0表示没有读取到数据
}

size_t nes_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream){
    // 在嵌入式环境中，通常不需要写入文件
    (void)ptr;
    (void)size;
    (void)nmemb;
    (void)stream;
    return 0;  // 返回0表示没有写入数据
}

int nes_fseek(FILE *stream, long int offset, int whence){
    // 在嵌入式环境中，ROM数据通过内存数组访问，不需要文件定位
    (void)stream;
    (void)offset;
    (void)whence;
    return -1;  // 返回-1表示操作失败
}

int nes_fclose(FILE *stream ){
    // 在嵌入式环境中，不需要关闭文件
    (void)stream;
    return 0;  // 返回0表示成功
}
#endif

#if (NES_ENABLE_SOUND == 1)
int nes_sound_output(uint8_t *buffer, size_t len){
    // 在嵌入式环境中，音频输出可以通过I2S或其他音频接口实现
    // 这里提供一个基础的实现框架
    (void)buffer;  // 暂时忽略音频数据
    (void)len;     // 暂时忽略数据长度
    
    // TODO: 实际项目中可以在这里添加音频输出代码
    // 例如：通过I2S接口输出音频数据
    // i2s_write_data(buffer, len);
    
    return 0;
}
#endif

int nes_initex(nes_t *nes){
    (void)nes;  // 避免未使用参数警告
    
    LCD_Init();

    // 初始化音频系统（如果需要）
    // 例如：初始化I2S接口用于音频输出
    // i2s_init();
    
    return 0;  // 成功初始化
}

int nes_deinitex(nes_t *nes){
    (void)nes;
    // 在嵌入式环境中通常不需要特殊的清理操作
    // 如果有需要，可以在这里添加清理代码
    // 例如：关闭音频接口、释放资源等
    return 0;
}

int nes_draw(int x1, int y1, int x2, int y2, nes_color_t* color_data)
{
    if (color_data == NULL) {
        return -1;
    }
    // 在嵌入式环境中，通过LCD接口绘制像素数据
    // 设置绘制窗口
    LCD_SetWindows(x1, y1, x2, y2);

    // 计算像素数量
    int width = x2 - x1 + 1;
    int height = y2 - y1 + 1;
    int pixel_count = width * height;
    // 直接使用16位RGB565颜色数据，逐个发送像素

    for (int i = 0; i < pixel_count; i++) {
        Lcd_WriteData_16Bit(color_data[i]);
    }

    LCD_Fill(0, 0, width, height, Color[i]);
    return 0;
}

void handle_input_events(nes_t* nes) {
    if (nes == NULL) {
        return;
    }
    
    // 基本的输入处理逻辑
    // 在嵌入式环境中，可以添加按键检测逻辑
    // 这里提供一个简单的实现，确保游戏能够继续运行
    // 模拟基本的游戏输入（可以根据实际硬件按键进行修改）
    // 例如：检测GPIO按键状态并更新手柄状态
    
    // 简单的自动输入模拟（让游戏能够自动进行）
    static uint32_t input_counter = 0;
    input_counter++;
    
    // 每隔一段时间模拟按键输入，防止游戏卡住
    if (input_counter % 120 == 0) {  // 每2秒模拟一次输入
        // 模拟按下START键（通常用于开始游戏或跳过画面）
        // 根据 nes_joypad_t 结构体，START1 对应 ST1 位
        nes->nes_cpu.joypad.ST1 = 1;
    } else if (input_counter % 120 == 10) {
        // 释放按键
        nes->nes_cpu.joypad.ST1 = 0;
    }
}

#define FRAMES_PER_SECOND   10  // 约16.67ms，60FPS

void nes_frame(nes_t* nes){
    // 帧率控制 - 延迟约16.67ms以维持60FPS
    osal_msleep(FRAMES_PER_SECOND);
}
